#include <stdlib.h>

#include <neko.h>
#include <neko_vm.h>

#include <iconv.h>

#include <string.h>

DEFINE_KIND(k_iconv_t);

value open(value from, value to) {
    iconv_t handle;
    const char *tostr, *fromstr;
    val_check(from, string);
    val_check(to, string);

    tostr = val_string(to);
    fromstr = val_string(from);

    handle = iconv_open(tostr, fromstr);

    if (((iconv_t) -1) == handle) {
        if (errno == EINVAL) {
            failure("Conversion not supported");
        } else {
            failure("ICONV: Unknown error");
        }
        neko_error();
    }

    return alloc_abstract(k_iconv_t, handle);
}

value close(value cd) {
    val_check_kind(cd, k_iconv_t);
   
    return alloc_int(iconv_close(val_data(cd)));
}

value convert(value cd, value source) {
    size_t buf_left, buf_size, src_left, result; 
    const char* src;
    char *buf, *buf_start;
    buffer output;
    iconv_t handle;

    val_check(source, string);
    val_check_kind(cd, k_iconv_t);

    output = alloc_buffer(NULL);
    src = val_string(source);
    src_left = buf_size = strlen(src);
    // allocating a buffer of the length of the original string
    buf_start = alloc_private((unsigned int)src_left);
    handle = val_data(cd);

    do {
        buf = buf_start;
        buf_left = buf_size;
        result = iconv(handle, &src, &src_left, &buf, &buf_left);
        buffer_append_sub(output, buf_start, buf - buf_start);
        if (result < 0) {
            switch (errno) {
                case EILSEQ: failure("Invalid multibyte sequence found"); neko_error();
                case EINVAL: failure("Incomplete multibyte sequence found"); neko_error();
            }
        }
    } while (src_left > 0);

    return buffer_to_string(output);
}

DEFINE_PRIM(open, 2)
DEFINE_PRIM(close, 1)
DEFINE_PRIM(convert, 2)
